<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=Edge">
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css" type="text/css">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" type="text/css">
    <link rel="stylesheet" href="./resources/prism/prism.css" type="text/css">
    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    <link rel="stylesheet" href="./resources/layout.css" type="text/css">
    
    <link rel="stylesheet" href="region-growing.css">
    <script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=fetch,requestAnimationFrame,Element.prototype.classList,URL"></script>
    <script src="./resources/zeroclipboard/ZeroClipboard.min.js"></script>
    <title>Region Growing</title>
  </head>
  <body>

    <header class="navbar" role="navigation">
      <div class="container">
        <div class="display-table pull-left" id="navbar-logo-container">
          <a class="navbar-brand" href="./"><img src="./resources/logo-70x70.png">&nbsp;OpenLayers Examples</a>
        </div>
        <!-- menu items that get hidden below 768px width -->
        <nav class='collapse navbar-collapse navbar-responsive-collapse'>
          <ul class="nav navbar-nav pull-right">
            <li><a href="../doc">Docs</a></li>
            <li><a class="active" href="index.html">Examples</a></li>
            <li><a href="../apidoc">API</a></li>
            <li><a href="https://github.com/openlayers/ol3">Code</a></li>
          </ul>
        </nav>
      </div>
    </header>

    <div class="container-fluid">

      <div id="latest-check" class="alert alert-warning alert-dismissible" role="alert" style="display:none">
        <button id="latest-dismiss" type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
        This example uses OpenLayers v<span>3.20.0</span>. The <a id="latest-link" href="#" class="alert-link">latest</a> is v<span id="latest-version"></span>.
      </div>

      <div class="row-fluid">
        <div class="span12">
          <h4 id="title">Region Growing</h4>
          <div id="map" class="map" style="cursor: pointer"></div>
<table class="controls">
  <tr>
    <td>Threshold: <span id="threshold-value"></span></td>
    <td><input id="threshold" type="range" min="1" max="50" value="20"></td>
  </tr>
</table>

        </div>
      </div>

      <div class="row-fluid">
        <div class="span12">
          <p id="shortdesc">Grow a region from a seed pixel</p>
          <div id="docs"><p>Click a region on the map. The computed region will be red.</p> <p>
  This example uses a <code>ol.source.Raster</code> to generate data
  based on another source.  The raster source accepts any number of
  input sources (tile or image based) and runs a pipeline of
  operations on the input data.  The return from the final
  operation is used as the data for the output source.
</p> <p>
  In this case, a single tiled source of imagery data is used as input.
  The region is calculated in a single &quot;image&quot; operation using the &quot;seed&quot;
  pixel provided by the user clicking on the map. The &quot;threshold&quot; value
  determines whether a given contiguous pixel belongs to the &quot;region&quot; - the
  difference between a candidate pixel&#39;s RGB values and the seed values must
  be below the threshold.
</p> <p>
  This example also shows how an additional function can be made available
  to the operation.
</p>
</div>
          <div id="api-links">Related API documentation: <ul class="inline"><li><a href="../apidoc/ol.Map.html" title="API documentation for ol.Map">ol.Map</a></li>,<li><a href="../apidoc/ol.View.html" title="API documentation for ol.View">ol.View</a></li>,<li><a href="../apidoc/ol.layer.Image.html" title="API documentation for ol.layer.Image">ol.layer.Image</a></li>,<li><a href="../apidoc/ol.layer.Tile.html" title="API documentation for ol.layer.Tile">ol.layer.Tile</a></li>,<li><a href="../apidoc/ol.proj.html" title="API documentation for ol.proj">ol.proj</a></li>,<li><a href="../apidoc/ol.source.BingMaps.html" title="API documentation for ol.source.BingMaps">ol.source.BingMaps</a></li>,<li><a href="../apidoc/ol.source.Raster.html" title="API documentation for ol.source.Raster">ol.source.Raster</a></li></ul></div>
        </div>
      </div>

      <div class="row-fluid">
        <div id="source-controls">
          <a id="copy-button"><i class="fa fa-clipboard"></i> Copy</a>
          <a id="jsfiddle-button"><i class="fa fa-jsfiddle"></i> Edit</a>
        </div>
        <form method="POST" id="jsfiddle-form" target="_blank" action="https://jsfiddle.net/api/post/library/pure/">
          <textarea class="hidden" name="js">function growRegion(inputs, data) {
  var image &#x3D; inputs[0];
  var seed &#x3D; data.pixel;
  var delta &#x3D; parseInt(data.delta);
  if (!seed) {
    return image;
  }

  seed &#x3D; seed.map(Math.round);
  var width &#x3D; image.width;
  var height &#x3D; image.height;
  var inputData &#x3D; image.data;
  var outputData &#x3D; new Uint8ClampedArray(inputData);
  var seedIdx &#x3D; (seed[1] * width + seed[0]) * 4;
  var seedR &#x3D; inputData[seedIdx];
  var seedG &#x3D; inputData[seedIdx + 1];
  var seedB &#x3D; inputData[seedIdx + 2];
  var edge &#x3D; [seed];
  while (edge.length) {
    var newedge &#x3D; [];
    for (var i &#x3D; 0, ii &#x3D; edge.length; i &lt; ii; i++) {
      // As noted in the Raster source constructor, this function is provided
      // using the &#x60;lib&#x60; option. Other functions will NOT be visible unless
      // provided using the &#x60;lib&#x60; option.
      var next &#x3D; next4Edges(edge[i]);
      for (var j &#x3D; 0, jj &#x3D; next.length; j &lt; jj; j++) {
        var s &#x3D; next[j][0], t &#x3D; next[j][1];
        if (s &gt;&#x3D; 0 &amp;&amp; s &lt; width &amp;&amp; t &gt;&#x3D; 0 &amp;&amp; t &lt; height) {
          var ci &#x3D; (t * width + s) * 4;
          var cr &#x3D; inputData[ci];
          var cg &#x3D; inputData[ci + 1];
          var cb &#x3D; inputData[ci + 2];
          var ca &#x3D; inputData[ci + 3];
          // if alpha is zero, carry on
          if (ca &#x3D;&#x3D;&#x3D; 0) {
            continue;
          }
          if (Math.abs(seedR - cr) &lt; delta &amp;&amp; Math.abs(seedG - cg) &lt; delta &amp;&amp;
              Math.abs(seedB - cb) &lt; delta) {
            outputData[ci] &#x3D; 255;
            outputData[ci + 1] &#x3D; 0;
            outputData[ci + 2] &#x3D; 0;
            outputData[ci + 3] &#x3D; 255;
            newedge.push([s, t]);
          }
          // mark as visited
          inputData[ci + 3] &#x3D; 0;
        }
      }
    }
    edge &#x3D; newedge;
  }
  return {data: outputData, width: width, height: height};
}

function next4Edges(edge) {
  var x &#x3D; edge[0], y &#x3D; edge[1];
  return [
    [x + 1, y],
    [x - 1, y],
    [x, y + 1],
    [x, y - 1]
  ];
}

var key &#x3D; &#x27;Your Bing Maps Key from http://www.bingmapsportal.com/ here&#x27;;

var imagery &#x3D; new ol.layer.Tile({
  source: new ol.source.BingMaps({key: key, imagerySet: &#x27;Aerial&#x27;})
});

var raster &#x3D; new ol.source.Raster({
  sources: [imagery.getSource()],
  operationType: &#x27;image&#x27;,
  operation: growRegion,
  // Functions in the &#x60;lib&#x60; object will be available to the operation run in
  // the web worker.
  lib: {
    next4Edges: next4Edges
  }
});

var rasterImage &#x3D; new ol.layer.Image({
  opacity: 0.7,
  source: raster
});

var map &#x3D; new ol.Map({
  layers: [imagery, rasterImage],
  target: &#x27;map&#x27;,
  view: new ol.View({
    center: ol.proj.fromLonLat([-119.07, 47.65]),
    zoom: 11
  })
});

var coordinate;

map.on(&#x27;click&#x27;, function(event) {
  coordinate &#x3D; event.coordinate;
  raster.changed();
});

var thresholdControl &#x3D; document.getElementById(&#x27;threshold&#x27;);

raster.on(&#x27;beforeoperations&#x27;, function(event) {
  // the event.data object will be passed to operations
  var data &#x3D; event.data;
  data.delta &#x3D; thresholdControl.value;
  if (coordinate) {
    data.pixel &#x3D; map.getPixelFromCoordinate(coordinate);
  }
});

function updateControlValue() {
  document.getElementById(&#x27;threshold-value&#x27;).innerText &#x3D; thresholdControl.value;
}
updateControlValue();

thresholdControl.addEventListener(&#x27;input&#x27;, function() {
  updateControlValue();
  raster.changed();
});
</textarea>
          <textarea class="hidden" name="css">table.controls td {
  min-width: 110px;
  padding: 2px 5px;
}
</textarea>
          <textarea class="hidden" name="html">&lt;div id&#x3D;&quot;map&quot; class&#x3D;&quot;map&quot; style&#x3D;&quot;cursor: pointer&quot;&gt;&lt;/div&gt;
&lt;table class&#x3D;&quot;controls&quot;&gt;
  &lt;tr&gt;
    &lt;td&gt;Threshold: &lt;span id&#x3D;&quot;threshold-value&quot;&gt;&lt;/span&gt;&lt;/td&gt;
    &lt;td&gt;&lt;input id&#x3D;&quot;threshold&quot; type&#x3D;&quot;range&quot; min&#x3D;&quot;1&quot; max&#x3D;&quot;50&quot; value&#x3D;&quot;20&quot;&gt;&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
</textarea>
          <input type="hidden" name="wrap" value="l">
          <input type="hidden" name="resources" value="https://openlayers.org/en/v3.20.0/css/ol.css,https://openlayers.org/en/v3.20.0/build/ol.js">
        </form>
        <pre><code id="example-source" class="language-markup">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;title&gt;Region Growing&lt;/title&gt;
    &lt;link rel="stylesheet" href="https://openlayers.org/en/v3.20.0/css/ol.css" type="text/css"&gt;
    &lt;!-- The line below is only needed for old environments like Internet Explorer and Android 4.x --&gt;
    &lt;script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=requestAnimationFrame,Element.prototype.classList,URL"&gt;&lt;/script&gt;
    &lt;script src="https://openlayers.org/en/v3.20.0/build/ol.js"&gt;&lt;/script&gt;
    &lt;style&gt;
      table.controls td {
        min-width: 110px;
        padding: 2px 5px;
      }
    &lt;/style&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;div id&#x3D;&quot;map&quot; class&#x3D;&quot;map&quot; style&#x3D;&quot;cursor: pointer&quot;&gt;&lt;/div&gt;
    &lt;table class&#x3D;&quot;controls&quot;&gt;
      &lt;tr&gt;
        &lt;td&gt;Threshold: &lt;span id&#x3D;&quot;threshold-value&quot;&gt;&lt;/span&gt;&lt;/td&gt;
        &lt;td&gt;&lt;input id&#x3D;&quot;threshold&quot; type&#x3D;&quot;range&quot; min&#x3D;&quot;1&quot; max&#x3D;&quot;50&quot; value&#x3D;&quot;20&quot;&gt;&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/table&gt;
    &lt;script&gt;
      function growRegion(inputs, data) {
        var image &#x3D; inputs[0];
        var seed &#x3D; data.pixel;
        var delta &#x3D; parseInt(data.delta);
        if (!seed) {
          return image;
        }

        seed &#x3D; seed.map(Math.round);
        var width &#x3D; image.width;
        var height &#x3D; image.height;
        var inputData &#x3D; image.data;
        var outputData &#x3D; new Uint8ClampedArray(inputData);
        var seedIdx &#x3D; (seed[1] * width + seed[0]) * 4;
        var seedR &#x3D; inputData[seedIdx];
        var seedG &#x3D; inputData[seedIdx + 1];
        var seedB &#x3D; inputData[seedIdx + 2];
        var edge &#x3D; [seed];
        while (edge.length) {
          var newedge &#x3D; [];
          for (var i &#x3D; 0, ii &#x3D; edge.length; i &lt; ii; i++) {
            // As noted in the Raster source constructor, this function is provided
            // using the &#x60;lib&#x60; option. Other functions will NOT be visible unless
            // provided using the &#x60;lib&#x60; option.
            var next &#x3D; next4Edges(edge[i]);
            for (var j &#x3D; 0, jj &#x3D; next.length; j &lt; jj; j++) {
              var s &#x3D; next[j][0], t &#x3D; next[j][1];
              if (s &gt;&#x3D; 0 &amp;&amp; s &lt; width &amp;&amp; t &gt;&#x3D; 0 &amp;&amp; t &lt; height) {
                var ci &#x3D; (t * width + s) * 4;
                var cr &#x3D; inputData[ci];
                var cg &#x3D; inputData[ci + 1];
                var cb &#x3D; inputData[ci + 2];
                var ca &#x3D; inputData[ci + 3];
                // if alpha is zero, carry on
                if (ca &#x3D;&#x3D;&#x3D; 0) {
                  continue;
                }
                if (Math.abs(seedR - cr) &lt; delta &amp;&amp; Math.abs(seedG - cg) &lt; delta &amp;&amp;
                    Math.abs(seedB - cb) &lt; delta) {
                  outputData[ci] &#x3D; 255;
                  outputData[ci + 1] &#x3D; 0;
                  outputData[ci + 2] &#x3D; 0;
                  outputData[ci + 3] &#x3D; 255;
                  newedge.push([s, t]);
                }
                // mark as visited
                inputData[ci + 3] &#x3D; 0;
              }
            }
          }
          edge &#x3D; newedge;
        }
        return {data: outputData, width: width, height: height};
      }

      function next4Edges(edge) {
        var x &#x3D; edge[0], y &#x3D; edge[1];
        return [
          [x + 1, y],
          [x - 1, y],
          [x, y + 1],
          [x, y - 1]
        ];
      }

      var key &#x3D; &#x27;Your Bing Maps Key from http://www.bingmapsportal.com/ here&#x27;;

      var imagery &#x3D; new ol.layer.Tile({
        source: new ol.source.BingMaps({key: key, imagerySet: &#x27;Aerial&#x27;})
      });

      var raster &#x3D; new ol.source.Raster({
        sources: [imagery.getSource()],
        operationType: &#x27;image&#x27;,
        operation: growRegion,
        // Functions in the &#x60;lib&#x60; object will be available to the operation run in
        // the web worker.
        lib: {
          next4Edges: next4Edges
        }
      });

      var rasterImage &#x3D; new ol.layer.Image({
        opacity: 0.7,
        source: raster
      });

      var map &#x3D; new ol.Map({
        layers: [imagery, rasterImage],
        target: &#x27;map&#x27;,
        view: new ol.View({
          center: ol.proj.fromLonLat([-119.07, 47.65]),
          zoom: 11
        })
      });

      var coordinate;

      map.on(&#x27;click&#x27;, function(event) {
        coordinate &#x3D; event.coordinate;
        raster.changed();
      });

      var thresholdControl &#x3D; document.getElementById(&#x27;threshold&#x27;);

      raster.on(&#x27;beforeoperations&#x27;, function(event) {
        // the event.data object will be passed to operations
        var data &#x3D; event.data;
        data.delta &#x3D; thresholdControl.value;
        if (coordinate) {
          data.pixel &#x3D; map.getPixelFromCoordinate(coordinate);
        }
      });

      function updateControlValue() {
        document.getElementById(&#x27;threshold-value&#x27;).innerText &#x3D; thresholdControl.value;
      }
      updateControlValue();

      thresholdControl.addEventListener(&#x27;input&#x27;, function() {
        updateControlValue();
        raster.changed();
      });
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
      </div>
    </div>

    <script src="./resources/common.js"></script>
    <script src="./resources/prism/prism.min.js"></script>
    <script src="loader.js?id=region-growing"></script>
  </body>
  <script>
  var packageUrl = 'https://raw.githubusercontent.com/openlayers/openlayers.github.io/build/package.json';
  fetch(packageUrl).then(function(response) {
    return response.json();
  }).then(function(json) {
    var latestVersion = json.version;
    document.getElementById('latest-version').innerHTML = latestVersion;
    var url = window.location.href;
    var branchSearch = url.match(/\/([^\/]*)\/examples\//);
    var cookieText = 'dismissed=-' + latestVersion + '-';
    var dismissed = document.cookie.indexOf(cookieText) != -1;
    if (!dismissed && /^v[0-9\.]*$/.test(branchSearch[1]) && '3.20.0' != latestVersion) {
      var link = url.replace(branchSearch[0], '/latest/examples/');
      fetch(link, {method: 'head'}).then(function(response) {
        var a = document.getElementById('latest-link');
        a.href = response.status == 200 ? link : '../../latest/examples/';
      });
      var latestCheck = document.getElementById('latest-check');
      latestCheck.style.display = '';
      document.getElementById('latest-dismiss').onclick = function() {
        latestCheck.style.display = 'none';
        document.cookie = cookieText;
      }
    }
  });
  </script>
</html>
